/*
 * Copyright 2012 Alain Pannetrat (L1L1@gmx.com)
 * 
 * This file is part of cookie-miner.
 * 
 * cookie-miner is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version.
 * 
 * cookie-miner is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License 
 * along with cookie-miner. If not, see http://www.gnu.org/licenses/.
 *
 * @version: $Id$
 *
 */
#include "http_packet.h"
#include <cerrno>
#include <sys/time.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>

/**********************************************************
 *
 * HttpPacket::Headline
 *
 */

bool HttpPacket::Headline::set(const std::string& s)
{
    size_t start;
    size_t end;

    _headline_parts[0].clear();
    _headline_parts[1].clear();
    _headline_parts[2].clear();

    end = s.find(' ');

    if (end == std::string::npos)
	return false;

    _headline_parts[0].assign(s,0,end);

    start = end+1; // skip space

    end = s.find_first_of(" \r\n",start);

    _headline_parts[1].assign(s,start,end-start);

    if (end!=std::string::npos && s[end]==' ')
    {
	start = end + 1; // skip space

	end = s.find_first_of("\r\n",start);

	_headline_parts[2].assign(s,start,end-start);
    }

    return true;
}

bool HttpPacket::Headline::set(const std::string& part0, 
			       const std::string& part1, 
			       const std::string& part2)
{
	_headline_parts[0] = part0;
	_headline_parts[1] = part1;
	_headline_parts[2] = part2;
	return true;
}

inline std::string HttpPacket::Headline::full() const
{
    if (_headline_parts[2].empty())
	return _headline_parts[0] + " " + _headline_parts[1];

    return _headline_parts[0] + 
	    " " + _headline_parts[1] + 
	    " " + _headline_parts[2];
}

const std::string& HttpPacket::Headline::part(int part) const
{
	if (part<0 || part>2)
	  return _empty_string;
	return _headline_parts[part];
}

bool HttpPacket::Headline::read_in(BufferIn* in) 
{
	std::string s;
	if (!in->read_line(s)) return false;
	return set(s); 
}

void HttpPacket::Headline::clear()
{
	_headline_parts[0].clear();	
	_headline_parts[1].clear();	
	_headline_parts[2].clear();	
}
	
/**********************************************************
 *
 * HttpPacket::Headers
 *
 */

bool string_caseless_equal(const std::string& a, const std::string& b)
{
	if (a.size()!=b.size()) return false;
	return (strncasecmp(a.data(),b.data(),a.size())==0);
}

bool HttpPacket::Headers::add_line(const std::string& line)
{
	std::string s(line);

	trim_eol(s);

	if (s.size()==0) return false;

	if ((s[0]==' ' || s[0]=='\t') && !_headers.empty())
	{
		_headers.back().second += s;
	}
	else
	{
		std::string key;
		std::string val;
		size_t pos = s.find(":");
		
		if (pos!=std::string::npos)
		{
			key = s.substr(0,pos);
			pos++;
			while (pos<s.size() && (s[pos]==' ' || s[pos]=='\t')) pos++;
			if (pos<s.size()) val = s.substr(pos);
			_headers.push_back(KeyValue(key,val));
		}
	}
	return true;
}

bool HttpPacket::Headers::erase(const std::string& s, unsigned index)
{
        KeyValueList::iterator iter = find(s, index);

        if (iter==end())
                  return false;
        _headers.erase(iter);
        return true;
}

unsigned HttpPacket::Headers::exists(const std::string& s) const
{
        unsigned count = 0;

	for (KeyValueList::const_iterator iter = begin(); iter!=end(); ++iter)
	{
		if (string_caseless_equal((*iter).first,s)) count++;
	}
        return count;
}

bool HttpPacket::Headers::set(const std::string& field, const std::string& value, unsigned index)
{
        KeyValueList::iterator iter = find(field, index);

	if (iter!=end()) 
    		(*iter).second = value;
  	else 
    		_headers.push_back(KeyValue(field,value));
	return true;
}

bool HttpPacket::Headers::set(const std::string& field, int value, unsigned index)
{
	char buf[16];
	sprintf(buf,"%i",value);
	return set(field,buf,index);
} 

KeyValueList::iterator HttpPacket::Headers::find(const std::string& field, unsigned index)
{
	KeyValueList::iterator iter = _headers.begin();
	unsigned count = 0;

	while (iter!=end())
	{
		if (string_caseless_equal(field,(*iter).first))
		{
			if (index == count) return iter;
			count++;
		}
		++iter;
	}
	return end();
}

KeyValueList::const_iterator HttpPacket::Headers::find(const std::string& field, unsigned index) const
{
	KeyValueList::const_iterator iter = _headers.begin();
	unsigned count = 0;

	while (iter!=end())
	{
		if (string_caseless_equal(field,(*iter).first))
		{
			if (index == count) return iter;
			count++;
		}
		++iter;
	}
	return end();
}

const std::string& HttpPacket::Headers::get(const std::string& field, unsigned index) const
{
	KeyValueList::const_iterator iter = find(field, index);

	if (iter==end())
		return _empty_string;

	return (*iter).second;
}

int HttpPacket::Headers::get_as_int(const std::string& field, unsigned index) const
{
	std::string str_value = get(field,index);
	
	if (str_value=="")
		return 0;
	
	return atoi(str_value.c_str());
}

KeyValueList::const_iterator HttpPacket::Headers::match_word(const std::string& field, const std::string& word) const
{
	KeyValueList::const_iterator iter;
	unsigned count = 0;
       
	while ((iter = find(field,count++))!=end())
	{
		if ((*iter).second.find(word)!=std::string::npos) return iter;
	}
	return end();
}

bool HttpPacket::Headers::read_in(BufferIn *in)
{
	std::string line;

	while (in->read_line(line))
	{
		if (add_line(line)==false)
		{
			logger.message(logger.LOG_DEBUG,"Http header was loaded");
			return true; /* we probably got to the final '\r\n' */
		}
	}
	logger.message(logger.LOG_WARNING,"Failed to load http header");
	return false;
}

bool HttpPacket::Headers::write_out(BufferOut *out) const
{
	// TODO:
	//   out->write_line(_headline);

	for (KeyValueList::const_iterator iter=begin();iter!=end();++iter)
	{
    		out->write_line((*iter).first + ": " + (*iter).second + "\r\n");
  	}
	return out->write_line("\r\n");
}

void HttpPacket::Headers::clear()
{
	_headers.clear();
}

/**********************************************************
 * 
 * HttpPacket
 *
 */

std::string HttpPacket::_empty_string = "";

HttpPacket::HttpPacket() :
        _content_length_limit((unsigned)(-1)), 	
	_content(NULL), 
	_content_size(0), 
	_content_max(0), 
	_packet_id(-1),
	_parent_packet_id(-1),
	_packet_timestamp(0,0),
	_source("undefined",0),
	_destination("undefined",0)
{
    struct timeval tv;
    gettimeofday(&tv,NULL);
    _packet_timestamp.set(tv.tv_sec,tv.tv_usec);
}


#define CONTENT_BLOCK_LENGTH 8192
/*
bool HttpPacket::content_read_in_write_out(BufferIn *in, BufferOut *out)
{
    unsigned char buf[CONTENT_BLOCK_LENGTH];
    unsigned content_length;
    unsigned content_length_total;
    unsigned chunk_length;
    unsigned chunk_extra_headers;
    unsigned rlen;
    std::string line;

    // 
    // CASE 1: Content length is set.
    //
    //         example:		HTTP/1.0 200 OK\r\n
    //         			Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n
    //         			Server: Apache/0.8.4\r\n
    //         			Content-Type: text/html\r\n
    //         			Content-Length: 59\r\n
    //         			Expires: Sat, 01 Jan 2000 00:59:59 GMT\r\n
    //         			Last-modified: Fri, 09 Aug 1996 14:21:40 GMT\r\n
    //				\r\n
    //         			...
    //
    if (headers().exists("Content-length"))
    {
        content_length = headers().get_as_int("Content-length");
	content_length_total = content_length;

	while (content_length)
	{
	    rlen = content_length<CONTENT_BLOCK_LENGTH?content_length:CONTENT_BLOCK_LENGTH;
	    if (!in->read_block(rlen,buf))
	    {
		logger.message(logger.LOG_WARNING,"Failed to read content from source (with %u bytes remaining)",content_length);
		return false;
	    }
	    content_add(rlen,buf);
	    content_length-=rlen;
	    if (out) out->write_block(rlen,buf);
	}

	logger.message(logger.LOG_DEBUG,"Read a total of %u bytes of content based on the 'Content-length' header",content_length_total);
	return true;
    }
    // 
    // CASE 2: Chunked transfer encoding
    //
    // 	       example:		HTTP/1.1 200 OK\r\n
    // 	       			Content-Type: text/plain\r\n
    // 	       			Transfer-Encoding: chunked\r\n
    //				\r\n
    // 				
    else if (headers().match_word("Transfer-encoding","chunked")!=headers().end())
    {
	content_length_total = 0;
	
	while (in->read_line(line))
	{
	    // line contains chunk size
	    if (out) out->write_line(line);

	    chunk_length = content_length = (unsigned)strtol(line.c_str(),NULL,16);
	    
	    while (content_length)
	    {
		rlen = content_length<CONTENT_BLOCK_LENGTH?content_length:CONTENT_BLOCK_LENGTH;
		if (!in->read_block(rlen,buf))
		{
		    logger.message(logger.LOG_WARNING,"Failed to read content from source (with %u bytes remaining in chunk)",content_length);
		    return false;
		}
		content_add(rlen,buf);
		content_length       -= rlen;
		content_length_total += rlen;
	    	if (out) out->write_block(rlen,buf);
	    }

	    // newline at end of chunck
	    in->read_line(line);
       	    if (out) out->write_line(line);
	    
	    if (chunk_length!=0)
	    {
		    if (line!="\r\n" && line!="\n")
		    {
			    logger.message(logger.LOG_WARNING,"Block does not end with CRLF in chunked encoding, aborting content loading.");
			    break;
		    }
	    }
	    else
	    {
		    chunk_extra_headers = 0;

		    while (line!="\r\n" && line!="\n")
		    {
			    chunk_extra_headers++;
			    headers().add_line(line);
			    in->read_line(line);
			    if (out) out->write_line(line);
		    }
		    break; // out of while
	    }
	}
	headers().erase("Transfer-encoding");
	headers().set("Content-length",content_length_total);

	logger.message(logger.LOG_DEBUG,"Read a total of %u bytes of content in chuncked encoding, with %u extra headers",content_length_total,chunk_extra_headers);
	
	return true;
    }
    // 
    // CASE 3: 	response has content-type defined and return code is 2xx
    //
    // 		example: 	HTTP/1.1 206 Partial content\r\n
    // 				Date: Wed, 15 Nov 1995 06:25:24 GMT\r\n
    // 				Last-modified: Wed, 15 Nov 1995 04:58:08 GMT\r\n
    // 				Content-type: multipart/byteranges; boundary=...\r\n
    // 				\r\n
    //
    else if (headers().exists("Content-type") && headline().part(1)[0]=='2' && headline().part(1)!="204")
    {
	content_length_total = 0;

	logger.message(logger.LOG_DEBUG,"No content-length or chunked encoding, but content-type is defined, processing response.");
	do {
	    rlen = CONTENT_BLOCK_LENGTH;
	    logger.message(logger.LOG_DEBUG,"Read attempt");
	    in->read_block(rlen,buf);
	    logger.message(logger.LOG_DEBUG,"Got %i bytes",rlen);
	    if (rlen>0)
	    {
		    content_add(rlen,buf);
		    content_length_total += rlen;
        	    if (out) out->write_block(rlen,buf);
	    }
	} while (rlen==CONTENT_BLOCK_LENGTH);
	
	logger.message(logger.LOG_DEBUG,"Read a total of %u bytes of content until end of file",
			content_length_total);
	
	return true;
    }
    // 
    // CASE 4: No content.
    //
    
    logger.message(logger.LOG_DEBUG,"No content detected");
    return true;
}*/

bool HttpPacket::content_read_in_write_out(BufferIn *in, BufferOut *out, bool no_content)
{
    unsigned char buf[CONTENT_BLOCK_LENGTH];
    unsigned content_length;
    unsigned content_length_total;
    unsigned chunk_length;
    unsigned chunk_extra_headers;
    unsigned rlen;
    std::string line;

    //////////////////////////////////
    // CASE 1, section 4.4, RFC2616 //
    // No content                   //
    //////////////////////////////////

    if (no_content || 
	headline().part(1)[0] == '1' ||
	headline().part(1) == "204" ||
	headline().part(1) == "304")
    {
    	logger.message(logger.LOG_DEBUG,"Packet with no expected content.");
	return true;
    }   

    //////////////////////////////////
    // CASE 2, section 4.4, RFC2616 //
    // Chunked content encoding.    //
    //////////////////////////////////

    if (headers().match_word("Transfer-encoding","chunked")!=headers().end())
    {
	content_length_total = 0;
	
	while (in->read_line(line))
	{
	    // line contains chunk size
	    if (out) out->write_line(line);

	    chunk_length = content_length = (unsigned)strtol(line.c_str(),NULL,16);
	    
	    while (content_length)
	    {
		rlen = content_length<CONTENT_BLOCK_LENGTH?content_length:CONTENT_BLOCK_LENGTH;
		if (!in->read_block(rlen,buf))
		{
		    logger.message(logger.LOG_WARNING,"Failed to read content from source (with %u bytes remaining in chunk)",content_length);
		    return false;
		}
		content_add(rlen,buf);
		content_length       -= rlen;
		content_length_total += rlen;
	    	if (out) out->write_block(rlen,buf);
	    }

	    // newline at end of chunck
	    in->read_line(line);
       	    if (out) out->write_line(line);
	    
	    if (chunk_length!=0)
	    {
		    if (line!="\r\n" && line!="\n")
		    {
			    logger.message(logger.LOG_WARNING,"Block does not end with CRLF in chunked encoding, aborting content loading.");
			    break;
		    }
	    }
	    else
	    {
		    chunk_extra_headers = 0;

		    while (line!="\r\n" && line!="\n")
		    {
			    chunk_extra_headers++;
			    headers().add_line(line);
			    in->read_line(line);
			    if (out) out->write_line(line);
		    }
		    break; // out of while
	    }
	}
	headers().erase("Transfer-encoding");
	headers().set("Content-length",content_length_total);

	logger.message(logger.LOG_DEBUG,"Read a total of %u bytes of content in chuncked encoding, with %u extra headers",content_length_total,chunk_extra_headers);
	
	return true;
    }

    //////////////////////////////////
    // CASE 3, section 4.4, RFC2616 //
    // Content-length is defined    //
    //////////////////////////////////

    if (headers().exists("Content-length"))
    {
        content_length = headers().get_as_int("Content-length");
	content_length_total = content_length;

	while (content_length)
	{
	    rlen = content_length<CONTENT_BLOCK_LENGTH?content_length:CONTENT_BLOCK_LENGTH;
	    if (!in->read_block(rlen,buf))
	    {
		logger.message(logger.LOG_WARNING,"Failed to read content from source (with %u bytes remaining)",content_length);
		return false;
	    }
	    content_add(rlen,buf);
	    content_length-=rlen;
	    if (out) out->write_block(rlen,buf);
	}

	logger.message(logger.LOG_DEBUG,"Read a total of %u bytes of content based on the 'Content-length' header",content_length_total);
	return true;
    }

    ////////////////////////////////////////////
    // Other cases where there's some content //
    ////////////////////////////////////////////

    if (headers().exists("Content-type"))
    {
	content_length_total = 0;

	logger.message(logger.LOG_DEBUG,"No content-length or chunked encoding, but content-type is defined, processing response.");
	do {
	    rlen = CONTENT_BLOCK_LENGTH;
	    logger.message(logger.LOG_DEBUG,"Read attempt");
	    in->read_block(rlen,buf);
	    logger.message(logger.LOG_DEBUG,"Got %i bytes",rlen);
	    if (rlen>0)
	    {
		    content_add(rlen,buf);
		    content_length_total += rlen;
        	    if (out) out->write_block(rlen,buf);
	    }
	} while (rlen==CONTENT_BLOCK_LENGTH);
	
	logger.message(logger.LOG_DEBUG,"Read a total of %u bytes of content until end of file",
			content_length_total);
	
	return true;
    }
    
    ///////////////
    // Catch all //
    ///////////////
    
    logger.message(logger.LOG_ERROR,"Could not handle content, assuming none.");
    return true;
}

bool HttpPacket::content_add(unsigned len, const unsigned char *content)
{
    unsigned char *odata;

    if (len==0 || _content_size>=_content_length_limit)
	return true;
    if (_content_size+len>=_content_length_limit)
	len = _content_length_limit - _content_size;

    if (_content==NULL)
    {
	_content_max = (len<8?8:len);
	_content = new unsigned char[_content_max];
	if (_content==NULL) return false;
    }
    else if (_content_max-_content_size<len)
    {
	while ((_content_max-_content_size)<len) _content_max<<=1;
	odata = new unsigned char[_content_max];
	if (_content==NULL) return false;
	memcpy(odata,_content,_content_size);
	delete [] _content;
	_content = odata;
    }
    memcpy(_content+_content_size,content,len);
    _content_size+=len;
    return true;

}

unsigned HttpPacket::content_length() const
{
  return _content_size;
}

const unsigned char *HttpPacket::content_get() const
{
  return _content;
}

bool HttpPacket::content_write_out(BufferOut *out) const
{
    unsigned content_size = _content_size;
    return out->write_block(content_size,_content);  
}

void HttpPacket::log() const
{
  char buf[80];
  KeyValueList::const_iterator iter;

  logger.message(logger.LOG_DEBUG,"http: - %s",headline().full().c_str());
  for (iter=headers().begin();iter!=headers().end();++iter)
  {
      std::string header = (*iter).first + ": " + (*iter).second + "\r\n";
      if (header.size()<80)
      {
	memcpy(buf,header.data(),header.size());
	buf[header.size()]=0;
      }
      else
      {
	memcpy(buf,header.data(),74);
	memcpy(buf+74,"(...)",6);
      }
      logger.message(logger.LOG_DEBUG,"http: + %s",buf);
  }
  //logger.message(logger.LOG_DEBUG,"http: ** %i bytes of content follow **",_content_size);  
}

void HttpPacket::clear()
{
	headline().clear();
	headers().clear();
	if (_content) delete [] _content;
	_content = NULL;
	_content_size = 0;
	_content_max = 0; 
	_packet_id = -1;
	_parent_packet_id = -1;
	_packet_timestamp.set(0,0);
	_source.set("undefined");
	_destination.set("undefined");
}

HttpPacket::~HttpPacket()
{
	if (_content) delete [] _content;
}


/************************************************************************************/
#define SQL_MAX_ATTEMPTS 10


bool HttpPacketDB::open(bool readonly)
{
    const char *stmt = "CREATE TABLE packets ("
	    			"packet_id INTEGER PRIMARY KEY,"
				"parent_id INTEGER,"
				"time_sec INTEGER,"
				"time_usec INTEGER,"
		        	"src_addr TEXT,"
			        "src_port INTEGER,"
			        "dst_addr TEXT,"
				"dst_port INTEGER,"
				"header BLOB,"
				"body BLOB );";
    char *errmsg;

    if (readonly)
    {
	if (sqlite3_open_v2(_db_name.c_str(),&_dbhandle,SQLITE_OPEN_READONLY,NULL)==SQLITE_OK)
	{
		sqlite3_busy_timeout(_dbhandle,60000);
		return true;
	}
	logger.message(logger.LOG_ERROR,"Error opening %s read-only.",_db_name.c_str());
	return false;
    }
    
    if (sqlite3_open_v2(_db_name.c_str(),&_dbhandle,SQLITE_OPEN_READWRITE,NULL)==SQLITE_OK)
    {
	    sqlite3_busy_timeout(_dbhandle,60000);
	    return true;
    }	

    if (sqlite3_open_v2(_db_name.c_str(),&_dbhandle,SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)!=SQLITE_OK)
    {
	logger.message(logger.LOG_ERROR,"Error creating %s.",_db_name.c_str());
	close();
	return false;
    }
    sqlite3_busy_timeout(_dbhandle,60000); 
    if (sqlite3_exec(_dbhandle,stmt,NULL,NULL,&errmsg)!=SQLITE_OK)
    {
	logger.message(logger.LOG_ERROR,"Error creating table in %s: %s",_db_name.c_str(),errmsg);
	sqlite3_free(errmsg);
	close();
	return false;
    }
    logger.message(logger.LOG_DEBUG,"Created SQL database %s",_db_name.c_str());
    return true;
}

int HttpPacketDB::top_id()
{
    int top_id;
    int err_code;

    sqlite3_stmt *compiled_stmt;

    if ((err_code=sqlite3_prepare_v2(_dbhandle,"SELECT MAX(packet_id) FROM packets;",-1,&compiled_stmt,NULL))!=SQLITE_OK)
    {
	logger.message(logger.LOG_ERROR,"Error preparing SQL statement in %s: %s(%i)",
		       _db_name.c_str(),
		       sqlite3_errmsg(_dbhandle),
		       err_code);
	return 0;
    }
    
    if ((err_code=sqlite3_step(compiled_stmt))==SQLITE_ROW)
	    top_id = sqlite3_column_int(compiled_stmt,0);
    else
    {
	    logger.message(logger.LOG_ERROR,
			    "Error executing SQL statement in %s: %s(%i)",
			    _db_name.c_str(),
			    sqlite3_errmsg(_dbhandle),
			    err_code);
	    top_id = -1;
    }
    sqlite3_finalize(compiled_stmt);
    return top_id;
}

bool HttpPacketDB::iterate(char *expression, bool(*iter_cb)(HttpPacket *,void *), void *params, iterate_how how)
{
    char *stmt;
    sqlite3_stmt *compiled_stmt;
    int packet_id;
    int parent_id;
    HttpPacket::Timestamp timestamp;
    SocketAddress source;
    SocketAddress destination;
    int err_code;
    bool ret_code;
    BufferInMemory *header;
    BufferInMemory *body;
    HttpPacket *packet;
    
    if (expression)
    	stmt = sqlite3_mprintf("SELECT * FROM packets WHERE %s;",expression);
    else
	stmt = sqlite3_mprintf("SELECT * FROM packets;");

    if ((err_code=sqlite3_prepare_v2(_dbhandle,stmt,-1,&compiled_stmt,NULL))!=SQLITE_OK)
    {
	logger.message(logger.LOG_ERROR,"Error preparing SQL statement in %s: %s(%i)",
		       _db_name.c_str(),
		       sqlite3_errmsg(_dbhandle),
		       err_code);
	sqlite3_free(stmt);
	return false;
    }
    sqlite3_free(stmt);

    for (;;)
    {
	err_code = sqlite3_step(compiled_stmt);
	if (err_code == SQLITE_DONE)
	{
		ret_code = true;
		break;
	}
	else if (err_code == SQLITE_ROW)
	{
	    packet_id = sqlite3_column_int(compiled_stmt,0);
	    parent_id = sqlite3_column_int(compiled_stmt,1);
	    
	    timestamp.sec((unsigned)sqlite3_column_int(compiled_stmt,2));
	    timestamp.usec((unsigned)sqlite3_column_int(compiled_stmt,3));
	    
	    source.ip((const char *)sqlite3_column_text(compiled_stmt,4));
	    source.port((unsigned)sqlite3_column_int(compiled_stmt,5));
	    
	    destination.ip((const char *)sqlite3_column_text(compiled_stmt,6));
	    destination.port((unsigned)sqlite3_column_int(compiled_stmt,7));

	    header = BufferInMemory::create((unsigned)sqlite3_column_bytes(compiled_stmt,8), 
					  (unsigned char *)sqlite3_column_blob(compiled_stmt,8));
	    if (how==ITERATE_HEADER_AND_CONTENT)
		    body = BufferInMemory::create((unsigned)sqlite3_column_bytes(compiled_stmt,9),
				    (unsigned char *)sqlite3_column_blob(compiled_stmt,9));
	    else
		    body = NULL;

	    packet = HttpPacket::create();
	    
	    packet->id(packet_id);
	    packet->parent_id(parent_id);
	    packet->timestamp(timestamp);
	    packet->source(source);
	    packet->destination(destination);

	    if (header)
	    {
		    packet->headline().read_in(header);
		    packet->headers().read_in(header);
		    delete header;
	    }
	    if (body) 
	    {
		    packet->content_read_in(body);
		    delete body;
	    }

	    iter_cb(packet,params);
	    delete packet;
	}
	else
	{
		logger.message(logger.LOG_ERROR,
				"Error executing SQL statement in %s: %s(%i)",
				_db_name.c_str(),
				sqlite3_errmsg(_dbhandle),
				err_code);
		ret_code = false;
		break;
	}
    }
    sqlite3_finalize(compiled_stmt);
    return ret_code;
}

bool HttpPacketDB::store(HttpPacket* packet)
{
    char *stmt;
    sqlite3_stmt *compiled_stmt;
    const HttpPacket::Timestamp& timestamp(packet->timestamp());
    BufferOutMemory *header;
    BufferOutMemory *body;
    int err_code;

    if (_dbhandle==NULL)
	return false;

    stmt = sqlite3_mprintf("INSERT INTO packets VALUES (NULL, %i, %u, %u, %Q, %u, %Q, %u, ?, ?);",
		    	    packet->parent_id(),
			    timestamp.sec(),
			    timestamp.usec(),
			    packet->source().ip(),
	 		    packet->source().port(),
			    packet->destination().ip(),
	 		    packet->destination().port()
			    );
       
    if ((err_code=sqlite3_prepare_v2(_dbhandle,stmt,-1,&compiled_stmt,NULL))!=SQLITE_OK)
    {
	logger.message(logger.LOG_ERROR,"Error creating SQL statement in %s: %s(%i)",
		       _db_name.c_str(),
		       sqlite3_errmsg(_dbhandle),
		       err_code);
	sqlite3_free(stmt); // release string
	return false;
    }
    sqlite3_free(stmt); // release string

    //
    // Prepare header blob
    //

    header = BufferOutMemory::create();
    packet->headline().write_out((BufferOut*)header);
    packet->headers().write_out((BufferOut*)header);
   
    if (sqlite3_bind_blob(compiled_stmt,1,header->get_data(),header->get_size(),SQLITE_TRANSIENT)!=SQLITE_OK)
    {
	logger.message(logger.LOG_ERROR,"Error adding header packet data in SQL statement in %s",
		       _db_name.c_str());
	sqlite3_finalize(compiled_stmt);
	delete header;
	return false;
    }

    // 
    // Prepare content blob
    //

    body = BufferOutMemory::create();
    packet->content_write_out((BufferOut*)body);
   
    if (sqlite3_bind_blob(compiled_stmt,2,body->get_data(),body->get_size(),SQLITE_TRANSIENT)!=SQLITE_OK)
    {
	logger.message(logger.LOG_ERROR,"Error adding body packet data in SQL statement in %s",
		       _db_name.c_str());
	sqlite3_finalize(compiled_stmt);
	delete header;
	delete body;
	return false;
    }


    err_code = sqlite3_step(compiled_stmt);
    if (err_code == SQLITE_OK || err_code == SQLITE_DONE) 
    {
	    logger.message(logger.LOG_DEBUG,"Saved packet in %s",
			    _db_name.c_str());
    }
    else
    {
	    logger.message(logger.LOG_ERROR,"Error storing packet in %s: %s(%i)",
			    _db_name.c_str(),
			    sqlite3_errmsg(_dbhandle),
			    err_code);
	    sqlite3_finalize(compiled_stmt);
	    delete header;
	    delete body;
	    return false;
    }
   
    sqlite3_finalize(compiled_stmt);
    
    packet->id((int)sqlite3_last_insert_rowid(_dbhandle));

    logger.message(logger.LOG_DEBUG,"Saved packet with packetid=%i",packet->id());

    delete header;
    delete body;
    return true;
}

void HttpPacketDB::close()
{
  if (_dbhandle)
      sqlite3_close(_dbhandle);
  _dbhandle = NULL;
}
														
std::string HttpPacketDB::_db_name = "packets.db";
/*
int main()
{
	HttpPacket::URL url("http://toto:tutu@www.example.com:8888/chemin/d/acc%C3%A8s.php?q=req&q2=req2#signet");

	fprintf(stderr,"OK\n"); fflush(stderr);

	fprintf(stderr,"%s\n",url.get().c_str());
}
*/

